package com.wans.common.redis;


import com.wans.common.constans.CustomExceptionEnum;
import com.wans.common.exception.CustomRunningException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
@SuppressWarnings("all")
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    // ============================== String 操作 ===============================

    /**
     * 设置字符串值（永不过期）
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置字符串值（带过期时间）
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间
     * @param unit    时间单位
     */
    public void set(String key, String value, long timeout, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 获取字符串值
     *
     * @param key 键
     * @return 值
     */
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 获取匹配的所有key
     * @param pattern 匹配值
     * @return 所有key
     */
    public Set<String> keys(String pattern) {
        return stringRedisTemplate.keys(pattern);
    }

    // ============================== 键操作 ===============================

    /**
     * 删除键
     *
     * @param keys 键（可批量）
     * @return 成功删除的数量
     */
    public Long delete(String... keys) {
        return stringRedisTemplate.delete(Arrays.asList(keys));
    }

    /**
     * 删除键
     *
     * @param keys 键（可批量）
     * @return 成功删除的数量
     */
    public Long delete(Set<String> keys) {
        return stringRedisTemplate.delete(keys);
    }

    /**
     * 判断键是否存在
     *
     * @param key 键
     * @return true-存在，false-不存在
     */
    public Boolean exists(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key     键
     * @param timeout 过期时间
     * @param unit    时间单位
     * @return true-成功，false-失败
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return stringRedisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取剩余过期时间（秒）
     *
     * @param key 键
     * @return 剩余时间（秒），-1为永不过期，-2为键不存在
     */
    public Long ttl(String key) {
        return stringRedisTemplate.getExpire(key);
    }

    // ============================== Hash 操作 ===============================

    /**
     * 设置Hash值
     *
     * @param key   键
     * @param hKey  Hash键
     * @param value 值
     */
    public void hSet(String key, String hKey, Object value) {
        stringRedisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash值
     *
     * @param key  键
     * @param hKey Hash键
     * @return Hash值
     */
    public Object hGet(String key, String hKey) {
        return stringRedisTemplate.opsForHash().get(key, hKey);
    }

    /**
     * 获取整个Hash结构
     *
     * @param key 键
     * @return Map结构
     */
    public Map<Object, Object> hGetAll(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    // ============================== List 操作 ===============================

    /**
     * 向列表左侧添加元素
     *
     * @param key   键
     * @param value 值
     * @return 列表长度
     */
    public Long lPush(String key, String value) {
        return stringRedisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 获取列表范围
     *
     * @param key   键
     * @param start 起始索引
     * @param end   结束索引
     * @return 元素列表
     */
    public List<String> lRange(String key, long start, long end) {
        return stringRedisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 原子的
     * @param key 键
     * @param shortValues list值
     */
    public void modifyList2(String key, List<String> shortValues) {
        // delete(key)操作会立即执行（因为delete方法有隐式exec()语义）
        stringRedisTemplate.delete(key);

        // 开启事务 - 阻塞命令的执行
        stringRedisTemplate.multi();

        for (String shortValue : shortValues) {
            stringRedisTemplate.opsForList().leftPush(key, shortValue);
        }
        // 结束事务
        stringRedisTemplate.exec();
    }


    /**
     * 原子的
     * @param key 键
     * @param shortValues list值
     */
    public void modifyList(String key, List<String> shortValues) {
        stringRedisTemplate.execute(new SessionCallback<>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.delete(key); // 删除加入事务

                for (String shortValue : shortValues) {
                    operations.opsForList().leftPush(key, shortValue);
                }

                List<Object> results = operations.exec();
                if (results == null || results.isEmpty()) {
                    throw new CustomRunningException(CustomExceptionEnum.Redis_PROCESS_EX);
                }
                return results;
            }
        });
    }

    // ============================== Set 操作 ===============================

    /**
     * 向Set添加元素
     *
     * @param key    键
     * @param values 值（可多个）
     * @return 成功添加的数量
     */
    public Long sAdd(String key, String... values) {
        return stringRedisTemplate.opsForSet().add(key, values);
    }

    /**
     * 获取Set所有成员
     *
     * @param key 键
     * @return 成员集合
     */
    public Set<String> sMembers(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    // ============================== 高级功能 ===============================

    /**
     * 原子性自增
     *
     * @param key   键
     * @param delta 增量
     * @return 自增后的值
     */
    public Long increment(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 原子性自减
     *
     * @param key   键
     * @param delta 减量
     * @return 自减后的值
     */
    public Long decrement(String key, long delta) {
        return stringRedisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 分布式锁（简单实现）
     *
     * @param lockKey    锁key
     * @param requestId  请求标识（解锁时验证）
     * @param expireTime 锁过期时间（秒）
     * @return 是否成功加锁
     */
    public Boolean tryLock(String lockKey, String requestId, long expireTime) {
        return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁key
     * @param requestId 请求标识
     * @return 是否成功解锁
     */
    public Boolean releaseLock(String lockKey, String requestId) {
        // 使用Lua脚本保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then "
                + "return redis.call('del', KEYS[1]) "
                + "else return 0 end";
        return stringRedisTemplate.execute(
                new DefaultRedisScript<>(script, Boolean.class),
                Collections.singletonList(lockKey),
                requestId
        );
    }
}